上次的幾篇文章中有教過ScrollView和SeekBar的基礎應用,讓大家對這兩個元件有一個基本的了解。這次我們就要結合這兩個元件做出一個可利用旁邊拉桿做拖拉的ScrollView。
這次跟以前不一樣,我們要先寫JAVA的部分,對ScrollView和SeekBar做覆寫等等UI設計時才能使用。
創建一個名為ObservableScrollView的Class引用ScrollView並做重寫
public class ObservableScrollView extends ScrollView {
public ScrollViewListener scrollViewListener = null;
public ObservableScrollView (Context context) {
super(context);
}
public ObservableScrollView (Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public interface ScrollViewListener {
void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
}
public ObservableScrollView (Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollViewListener(ScrollViewListener scrollViewListener) {
this.scrollViewListener = scrollViewListener;
}
@Override
public void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
}
創建一個名為VerticalSeekbar的Class引用SeekBar並做重寫
public class VerticalSeekbar extends SeekBar {
public VerticalSeekbar(Context context) {
super(context);
}
public VerticalSeekbar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public VerticalSeekbar(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw);
}
@Override
public synchronized void setProgress(int progress) // it is necessary for calling setProgress on click of a button
{
super.setProgress(progress);
onSizeChanged(getWidth(), getHeight(), 0, 0);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
protected void onDraw(Canvas c) {
c.rotate(90);//旋轉
c.translate(0, -getWidth());//旋轉,這兩行不可去掉
super.onDraw(c);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
setProgress((int) (getMax() * event.getY() / getHeight()));
onSizeChanged(getWidth(), getHeight(), 0, 0);
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return true;
}
}
創建一個名為ScrollBindHelper的Class,用來連結剛剛所創建的ScrollView和SeekBar,其功能主要為將SeekBar拖動的百分比換算並對應到ScrollView,但它還需要兩個工具的輔助,等等一樣會寫在下面。
public class ScrollBindHelper implements SeekBar.OnSeekBarChangeListener,ObservableScrollView.ScrollViewListener{
private final VerticalSeekbar seekBar;
private final ObservableScrollView scrollView;
private final View scrollContent;
/**
* 使用靜態方法來繫結邏輯,程式碼可讀性更高。
*/
public ScrollBindHelper(VerticalSeekbar seekBar, ObservableScrollView scrollView) {
this.seekBar = seekBar;
this.scrollView = scrollView;
this.scrollContent = scrollView.getChildAt(0);
}
private boolean isUserSeeking;
private int getContentRange() {
seekBar.setMax(scrollContent.getHeight() - scrollView.getHeight());
int Range=scrollView.getScrollY();
return Range;
}
private int getScrollRange() {
System.out.println(scrollContent.getHeight() - scrollView.getHeight());
return scrollContent.getHeight() - scrollView.getHeight();
}
public ScrollBindHelper bind(VerticalSeekbar seekBar, ObservableScrollView scrollView) {
ScrollBindHelper helper = new ScrollBindHelper(seekBar, scrollView);
seekBar.setOnSeekBarChangeListener(helper);
scrollView.setScrollViewListener(helper);
return helper;
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (!fromUser) {
//將拖動的換百分比算成Y值,並對映到SrollView上。
scrollView.scrollTo(0, progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
isUserSeeking = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
isUserSeeking = false;
}
@Override
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
showScroll();
if (isUserSeeking) {
return;
}
//計算當前滑動位置相對於整個範圍的百分比,並對映到SeekBar上
int range = getContentRange();
seekBar.setProgress(range != 0 ? range : 0);
}
private void showScroll() {
seekBar.setVisibility(View.VISIBLE);
}
}
寫完上面那些就可以來設計UI了,在此我使用了兩個自己所設計的元件,分別是com.hr.myapplication3.ObservableScrollView和com.hr.myapplication3.VerticalSeekbar
前面com.hr.myapplication3的部分要根據自己專案的資料夾來做更改
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.hr.myapplication3.ObservableScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="15"
android:scrollbars="none" >
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="" />
</com.hr.myapplication3.ObservableScrollView>
<com.hr.myapplication3.VerticalSeekbar
android:id="@+id/seekbar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:progressDrawable="@drawable/seek" />
</LinearLayout>
在回到JAVA程式設計的部分,上述的前置工作都做完以後,終於可以來設計主頁面MainActivity了。
因為將ScrollView和SeekBar邦定的部分都在ScrollBindHelper中做好了,所以不需要在MainActivity多寫甚麼,我們只要宣告ScrollBindHelper並將ScrollView和SeekBar作為參數帶入就可以了。
public class MainActivity extends AppCompatActivity {
private TextView textView;
private ScrollBindHelper scrollBindHelper;
private VerticalSeekbar seekBar;
private ObservableScrollView scrollView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
for(int i = 0;i<99;i++){
textView.append("第" + i + "行\n");
}
seekBar = findViewById(R.id.seekbar);
scrollView = findViewById(R.id.scrollView);
scrollBindHelper=new ScrollBindHelper(seekBar,scrollView);
scrollBindHelper.bind(seekBar,scrollView);
scrollView.setOnTouchListener(new View.OnTouchListener() {
private int lastY = 0;
private int touchEventId = -9983761;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
View scroller = (View) msg.obj;
if (msg.what == touchEventId) {
if (lastY == scroller.getScrollY()) {
handleStop(scroller);
} else {
handler.sendMessageDelayed(handler.obtainMessage(touchEventId, scroller), 5);
lastY = scroller.getScrollY();
}
}
}
};
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
handler.sendMessageDelayed(handler.obtainMessage(touchEventId, v), 5);
}
return false;
}
private void handleStop(Object view) {
ScrollView scroller = (ScrollView) view;
int scrollY = scroller.getScrollY();
System.out.println("scrollY" + scrollY);
}
});
}
}